/**
 * @file Main.c
 * @brief Main GDR program and supporting functions
 * @author Matt Stallmann @date 29 Dec 2001
 * $Id: Main.c 10 2007-06-28 20:08:57Z mfms $
 */

/*
 *  Copyright (C) 1989, 2001, 2007  Matthias Stallmann, Prashant Hebbar.
 *  Contact: matt_stallmann@ncsu.edu
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program (file COPYING.txt); if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
 *  USA.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include "All.h"

/* global variable definitions */
I_VX_NODE vx_array[MAX_VERTEX]; /**< pointer for adjacency list array */
Display  *disp;
GC    rubber_gc;                /**< default graphics context */
GC    copy_gc;                  /**< graphics context for copying pixmaps */
GC    erase_gc;                 /**< ... and for erasing them */
GC    hilite;                   /**< for highlighted edges */
GC    help_gc;                  /**< for messages in help window */
GC    vlab_gc;                  /**< for vertex labels */
GC    elab_gc;                  /**< for edge labels */
GC    msg_gc;                   /**< for programmer messages */
XFontStruct *vlab_font_struct;  /**< structures for the three fonts */
XFontStruct *elab_font_struct;
XFontStruct *msg_font_struct;
int   vlab_yoffset;             /**< y offset for the fonts (ascent +
                                   vertical padding) */
int   elab_yoffset;
int   msg_yoffset;
int   vlab_height;              /**< height (per line) for labels, message
                                   window (ascent + descent + 2 *
                                   vertical padding) */
int   elab_height;
int   msg_height;

/* the main window, canvas, help window, popup menu panel, and resume button */
Window  main_window;
Window  main_canvas;
Pixmap  main_pixmap;            /**< pixmap storing image from main canvas */
Window  help_window;
Window  menu_window;
Window  resume_window;

unsigned long  black,white;      /**< black and white pixels */

/* cursors for move, drag, panel/menu, and normal select functions */
Cursor   mv_cur, label_curs, pane_cur, sel_curs;

XEvent   eve;                   /**< current X11 event */
int      screen;                /**< current screen */

int number_of_vertices = 0;     /**< maximum vertex index + 1 */

#ifdef DIRECTED
int direct=1;  /**< global: this means it's a digraph */
#else
int direct=0;  /**< global: this means the graph is undirected */
#endif

int pane_indx;                  /**< index of the current panel selection */
char *icone;                    /**< the title & icon string */
H_NODE * all_edges;             /**< the list of all edges */

int main(int argc, char **argv)
{
  void init_display(int argc, char **argv);
  void init_structures(void);
  void init_help_messages(void);
  void close_display(void);
  void while_not_done(void);
  FILE * fd;
  
  init_structures();
  init_help_messages();
  init_display(argc,argv);

  if ( argc > 1 && argv[1] != NULL ) {
    if ( (fd = fopen( argv[1], "r" )) == NULL ) {
      fprintf( stderr, "Unable to open file %s\n", argv[1] );
      exit( 1 );
    }
    if ( ! read_in_gr( fd ) )
      draw_gr();
    else clear_graph( 0, number_of_vertices ); 
  }

  /* the main loop */
  while_not_done();  
  close_display();

  return 0;
} /* main ends */

/**
   while_not_done -- Keeps checking for and handling events.
*/
void while_not_done(void)
{
  bool   quit = false;
  char   ans[3];  /* for XLookupString */
  KeySym key;
  
  while ( ! quit ) {
    XNextEvent( disp, &eve );
    if ( handle_background_event(eve) ) continue;

    /* ASSERT: eve is not an Expose event for any window in this
       application */
    switch ( eve.type ) {
    case ButtonPress:
      /* if 1st mouse button clicked on panel, highlight panel accordingly
         and assign the correct funtion to future actions */
      if ( eve.BUT == LEFT_BUTTON && is_panel_window( eve.WIN ) )
        act_pane(eve.YC);
      /* if clicked on the resume button, exit loop at this level */
      else if ( eve.WIN == resume_window )
        quit = true;
      /* pop the popup menu for button RIGHT_BUTTON */
      else if ( eve.BUT == RIGHT_BUTTON )
        quit = p_menu( eve.XC, eve.YC );
      /* if none of the above, then pass it on to the current function */ 
      else 
        quit = (*funct[pane_indx])();
      break;
    case EnterNotify :
      /* this is only relevant if in edit mode */
      if ( pane_indx == EDIT_LABEL )
        (*funct[pane_indx])();
      break;
    case KeyPress :
      /* 3 is the length of buffer 'ans'
         0 is to disable 'Compose Status' */
      XLookupString( (XKeyEvent *) &eve, ans, 3, &key, 0 );
      /* clear graph if 'c' or 'C' is pressed */
      if ( key == XK_c || key == XK_C ) {
        clear_graph_disp();
      }
      /* initialize graph if 'i' or 'I' is pressed (make everything
         unhighlighted, expose only the labels that are non-empty, and
         display all edges), i.e. initialize the display */
      else if ( key == XK_i || key == XK_I ) {
        init_display_attributes();
      }
      /* redraw graph if 'l' or 'L' is pressed */
      else if ( key == XK_l || key == XK_L ) {
        draw_gr();
      }
      /* quit if 'q' or 'Q' pressed */
      else if ( key == XK_q || key == XK_Q ) {
        quit = true;
      }
      /* press p or P for program */ 
      else if ( key == XK_p || key == XK_P ) {
        signal( SIGINT, sig_handler ); /* call handler in case of Ctrl-C */
        animat();
      }
      /* press r or R for read */   
      else if ( key == XK_r || key == XK_R ) {
        load_with_prompt( 0, 0 );
      }
      /* press s or S for save */
      else if ((key == XK_s) || (key == XK_S)) {
        save_with_prompt( 0, 0 );
      }
      break;
    } /* end, switch on event type */
  } /* end, while( ! exit ) */
} /* while_not_done ends */

int num_help_messages[HELP_CATEGORIES]; /* gives number of messages for
                                           each category */
char *help[HELP_CATEGORIES * MSG_PER_CATEGORY]; /* the help message pointers */

void init_help_messages( void ) {
  int i;
  num_help_messages[PANEL_HELP] = PANEL_TOPICS;
  num_help_messages[MENU_HELP] = MENU_TOPICS;
  num_help_messages[OTHER_HELP] = OTHER_TOPICS;

  for ( i = 0; i < HELP_CATEGORIES * MSG_PER_CATEGORY; ++i ) {
    help[ i ] = NULL;
  }

  /* The help messages for the panel */
  help[PANEL_HELP * MSG_PER_CATEGORY + CREATE_VERTEX] = 
    " Click LEFT for new Vertex"; 
  help[PANEL_HELP * MSG_PER_CATEGORY + CREATE_EDGE] =
    " Click LEFT on end points for new Edge";
  help[PANEL_HELP * MSG_PER_CATEGORY + MOVE] =
    " Drag vertex, edge label, or knot using MIDDLE";
  help[PANEL_HELP * MSG_PER_CATEGORY + DELETE] =
    " Click LEFT on vertex or edge to delete";
  help[PANEL_HELP * MSG_PER_CATEGORY + EDIT_LABEL] =
    " Place pointer in label and edit using keyboard";
  help[PANEL_HELP * MSG_PER_CATEGORY + POPUP_MENU] =
    " Use left or right mouse button to pop menu";
  
  /* ...for the popup menu */    
  help[MENU_HELP * MSG_PER_CATEGORY + SAVE] = "Save the graph to a file";
  help[MENU_HELP * MSG_PER_CATEGORY + READ] = "Read graph from a file";
  help[MENU_HELP * MSG_PER_CATEGORY + REDRAW] = "Redraw the graph";
  help[MENU_HELP * MSG_PER_CATEGORY + INIT] = "Re-init display attributes";
  help[MENU_HELP * MSG_PER_CATEGORY + CLEAR]
    = "Clear the window and the graph";
  help[MENU_HELP * MSG_PER_CATEGORY + RUN] = "Run the algorithm animation";
  help[MENU_HELP * MSG_PER_CATEGORY + QUIT] = "Quit GDR";

  /* Other help messages */
  help[OTHER_HELP * MSG_PER_CATEGORY + GENERAL] =
    " LEFT: select the mode; <Ctrl-C>: Abort; RIGHT: Pop up menu";
  help[OTHER_HELP * MSG_PER_CATEGORY + LOOP_PROMPT] =
    " Loop - Click to select 2 knot points";
  help[OTHER_HELP * MSG_PER_CATEGORY + TOO_MANY_VERTICES] =
    " Too many vertices";
  help[OTHER_HELP * MSG_PER_CATEGORY + SELECT_VERTEX] =
    " Click LEFT to select a vertex; Q or q to quit";
  help[OTHER_HELP * MSG_PER_CATEGORY + NO_HELP] =
    " No help available for this item! Possible bug.";
}

/**
   Draws a help string indicated by category and message_index in the
   top area of the  main window.
*/
void show_help( int category, int message_index ) {
  char * help_message;
  XMapRaised(disp, help_window);
  if ( category < 0 || category >= HELP_CATEGORIES
       || message_index < 0 || message_index >= num_help_messages[category]
       ) {
    category = OTHER_HELP;
    message_index = NO_HELP;
  }
  help_message = help[category * MSG_PER_CATEGORY + message_index];
  if ( help_message == NULL ) help_message = ""; /* safety valve */
  XClearWindow(disp, help_window);
  DRAW_STR(help_window, HPAD, CH_H + VPAD, help_message);
} /* show_help() ends */

/**
   Initialises the X Window Stuff
*/
void init_display(int argc, char **argv)
{
  XSetWindowAttributes  attr_str;
  XSizeHints      hint;
  XGCValues    values;
  unsigned long      valuemask;
  Font                  default_font;
  
  int orig_x, orig_y, width, height, border;

  /* for getting font dimension using XTextExtents (these are not used) */
  int direction_hint;
  int font_ascent;
  int font_descent;
  XCharStruct overall;

  /* window settings */
  /* setup display connect. and screen */
  if( ! (disp = XOpenDisplay("")) ) {
    fprintf(stderr, "Unable to open display.\n");
    abort();
  }
  screen = DefaultScreen(disp);
  /* init the values for 'white' color & 'black' - these are
     globals used by other functions also */
  white = WhitePixel(disp,screen);
  black = BlackPixel(disp,screen);
  /* the 'hint' to window manager - doesnt work for awm */
  hint.x = WIN_X; hint.y = WIN_Y;
  hint.width  = WIN_WID + PANEL_WIDTH + 2 * WINDOW_BORDER_WIDTH;
  hint.height = WIN_HYT + HELP_HEIGHT + 2 * WINDOW_BORDER_WIDTH;
  hint.flags = PPosition | PSize;
  /* create the main GDR window */
  main_window = XCreateSimpleWindow(disp, DefaultRootWindow(disp),
                                    hint.x, hint.y, hint.width, hint.height,
                                    WINDOW_BORDER_WIDTH, black, white);
  main_canvas = XCreateSimpleWindow(disp, main_window,
                                    PANEL_WIDTH, HELP_HEIGHT,
                                    WIN_WID, WIN_HYT,
                                    WINDOW_BORDER_WIDTH, black, white);
  help_window = XCreateSimpleWindow(disp, main_window,
                                    PANEL_WIDTH, 0,
                                    HELP_WIDTH, HELP_HEIGHT,
                                    0, black, white);

  /* Set the attributes for the main window and main canvas */
  /* for resize events */
  attr_str.bit_gravity = NorthWestGravity;
  /* set the mask for attribute changes */
  valuemask = CWBitGravity;
  XChangeWindowAttributes(disp, main_window, valuemask, &attr_str);
  XChangeWindowAttributes(disp, main_canvas, valuemask, &attr_str);
  /* set the inputs for the main canvas */
  XSelectInput(disp, main_canvas, OwnerGrabButtonMask | ButtonPressMask |
               ButtonReleaseMask | KeyPressMask | ExposureMask);
               
  /* ...and the main window (only pay attention to resize events) */
  XSelectInput(disp, main_window, StructureNotifyMask); 

  /* create the main_pixmap to serve as backup storage for the main window
     (for correct handling of expose events); all drawing is done to the
     pixmap and the window -- the window is redrawn from the pixmap in
     response to an expose event */
  main_pixmap = XCreatePixmap(disp, main_canvas, MAX_DISPLAY_WIDTH,
                              MAX_DISPLAY_HEIGHT,
                              XDefaultDepth(disp, XDefaultScreen(disp)));
  
  /* GC settings */
  valuemask = GCFunction | GCPlaneMask | GCForeground | GCBackground;
  values.function = GXxor;
  values.plane_mask = black^white;
  values.foreground = ALL_ON;
  values.background = ALL_OFF;
  /* the main GC in GDR */
  rubber_gc = XCreateGC(disp, main_canvas, valuemask, &values);
  XSetLineAttributes(disp, rubber_gc, EDGE_THICKNESS,
                     LineSolid, CapRound, JoinRound);
  /* load and establish the default font */
  default_font = XLoadFont(disp, FONT);
  XSetFont(disp, rubber_gc, default_font);

  /* hilite gc: used for highlighting edges  - the values are not 
     changed from rubber_gc */
  hilite = XCreateGC(disp, main_canvas, valuemask, &values);
  XSetLineAttributes(disp, hilite, HIGHLIGHT_THICKNESS,
                     LineSolid, CapRound, JoinRound);

  /* separate GC's for help, vertex labels, edge labels, messages */
  help_gc = XCreateGC(disp, help_window, valuemask, &values);
  vlab_gc = XCreateGC(disp, main_canvas, valuemask, &values);
  elab_gc = XCreateGC(disp, main_canvas, valuemask, &values);
  msg_gc = XCreateGC(disp, main_canvas, valuemask, &values);

  /* copy_gc: used to copy from pixmap to pixmap or window */
  copy_gc = XCreateGC(disp, main_canvas, 0, 0);
  XSetState(disp, copy_gc, black, white, GXcopy, black^white);

  /* erase_gc: used to clear a pixmap */
  erase_gc = XCreateGC(disp, main_canvas, 0, 0);
  XSetState(disp, erase_gc, black, white, GXcopyInverted, black^white);
  XFillRectangle(disp, main_pixmap, erase_gc, 0, 0, MAX_DISPLAY_WIDTH,
                 MAX_DISPLAY_HEIGHT);

  /* set up fonts for vertex labels, edge labels, and messages */
  vlab_font_struct = XLoadQueryFont(disp,VLAB_FONT);
  if (vlab_font_struct == 0) {
    fprintf(stderr,"\nNonexistent font: %s\n",VLAB_FONT);
    abort();
  }
  elab_font_struct = XLoadQueryFont(disp,ELAB_FONT);
  if (elab_font_struct == 0) {
    fprintf(stderr,"\nNonexistent font: %s\n",ELAB_FONT);
    abort();
  }
  msg_font_struct = XLoadQueryFont(disp,MSG_FONT);
  if (msg_font_struct == 0) {
    fprintf(stderr,"\nNonexistent font: %s\n",MSG_FONT);
    abort();
  }
  XTextExtents(vlab_font_struct, "", 0, &direction_hint,
               &font_ascent, &font_descent, &overall);
  vlab_yoffset = font_ascent + VLAB_VPAD;
  vlab_height = font_ascent + font_descent + 2 * VLAB_VPAD;
  XTextExtents(elab_font_struct, "", 0, &direction_hint,
               &font_ascent, &font_descent, &overall);
  elab_yoffset = font_ascent + ELAB_VPAD;
  elab_height = font_ascent + font_descent + 2 * ELAB_VPAD;
  XTextExtents(msg_font_struct, "", 0, &direction_hint,
               &font_ascent, &font_descent, &overall);
  msg_yoffset = font_ascent + MSG_VPAD;
  msg_height = font_ascent + font_descent + 2 * MSG_VPAD;
  XSetFont(disp, help_gc, default_font);
  XSetFont(disp, vlab_gc, vlab_font_struct->fid);
  XSetFont(disp, elab_gc, elab_font_struct->fid);
  XSetFont(disp, msg_gc, msg_font_struct->fid);

  /* Cursor settings */
  /* the move cursor */
  mv_cur = XCreateFontCursor(disp,XC_dot);
  /* the label cursor */
  label_curs = XCreateFontCursor(disp,XC_pencil);
  /* normal cursor - for select, etc */
  sel_curs = XCreateFontCursor(disp,XC_draft_small);
  XDefineCursor(disp, main_canvas, sel_curs);
  
  /* to initialize pixmap before main window is drawn */
  XFillRectangle(disp, main_pixmap, erase_gc, 0, 0, hint.width, hint.height);

  XMapRaised(disp, main_window);
  XMapRaised(disp, main_canvas);
#ifdef ICON
  icone = ICON;
#else
  icone = "** GDR **";
#endif
  XSetStandardProperties(disp, main_window, icone, icone, None, argv,
                         argc, &hint);
  
  /* the continue button */
  orig_x = 0;  orig_y = 0;
  width = strlen("RESUME ?") * CH_W + 2 * HPAD;
  height = CH_H + 2 * VPAD;
  border = 1;
  resume_window = XCreateSimpleWindow(disp, main_canvas, orig_x, orig_y,
                                      width, height, border, black, white);
  XSelectInput(disp, resume_window, OwnerGrabButtonMask | ButtonPressMask |
               ExposureMask);
  DRAW_STR(resume_window, HPAD, CH_H + VPAD, "RESUME ?");

  /* init. the menu panel stuff */
  pane_init();
} /* init_display ends */

void init_structures( void )
{
  int i;
  /* initialize the list of all edges to be empty */
  all_edges = new_list();
  /* initialize all adjacency lists to be null pointers */
  for( i = 0; i < MAX_VERTEX; ++i ) {
    vx_array[ i ].my_adj_list = NULL;
  }
} /* init_structures ends */

/**
   Closes down the GDR application , frees  space etc.
*/
void close_display( void ) {
  XFreeCursor(disp, sel_curs);
  XFreeCursor(disp, mv_cur);
  XFreeCursor(disp, label_curs);
  XFreeGC(disp, rubber_gc);
  XFreeGC(disp, hilite);
  XFreeGC(disp, help_gc);
  XFreeGC(disp, vlab_gc);
  XFreeGC(disp, elab_gc);
  XFreeGC(disp, msg_gc);
  XDestroyWindow(disp, main_window);
  XCloseDisplay(disp);
} /* close_display ends */

/** Highlight the i-th menu item */
static void highlight_menu_item(int i) {
  XFillRectangle( disp, menu_window, rubber_gc, 1,
                  MENU_ITEM_TOP( i, CH_H, VPAD ),
                  MW - 2, CH_H + VPAD );
}

/**
  Responsible for pop-up/pop_down of the menu on button RIGHT_BUTTON and
  calling the exec_top function.

  Returns true if 'QUIT' was chosen, else returns false. 
*/
int p_menu(int xc, int yc) {
  XEvent m_ev;                  /* menu change event */
  static bool first = true;     /* is this the first time? */
  static char *m_top[MENU_TOPICS]; /* array of menu topics */
  bool   holding_button = true;
  int    new_topic;
  int    cur_topic = NULL_TOPIC; /* initialize to an "empty" topic */
  int    i;
  
  if (first){
    m_top[ SAVE ] = "(S)AVE";
    m_top[ READ ] = "(R)EAD";
    m_top[ REDRAW ] = "REDRAW (L)";
    m_top[ INIT ] = "RE-(I)NIT";
    m_top[ CLEAR ] = "(C)LEAR";
    m_top[ RUN ] = "RUN (P)ROGRAM";
    m_top[ QUIT ] = "(Q)UIT GDR";
    
    menu_init();     /* create menu window */
    first = false;
  }
  
  XMoveWindow( disp, menu_window, xc, yc );
  XMapRaised( disp, menu_window );
  XWarpPointer( disp, None, menu_window, 0, 0, 0, 0, M_HPAD,
                MENU_TXT_BASE( 0, CH_H, VPAD ) );
  /* display the options */
  for( i = 0; i < MENU_TOPICS; i++ )
    DRAW_STR( menu_window, M_HPAD, MENU_TXT_BASE( i, CH_H, VPAD ), m_top[i] );
  /* catch the mouse moves */
  while ( holding_button ) {
    XNextEvent( disp, &m_ev );
    if ( handle_background_event( m_ev ) ) continue;
    switch( m_ev.type ) {
    case MotionNotify:
      new_topic = MENU_ITEM_FROM_Y( m_ev.YC, CH_H, VPAD );
      if( new_topic != cur_topic ) {
        if ( cur_topic >= 0 && cur_topic < MENU_TOPICS ) {
          highlight_menu_item( cur_topic ); /* erase highlighting from
                                               current topic if necessary */
        }
        if ( new_topic >= 0 && new_topic < MENU_TOPICS ) {
          highlight_menu_item( new_topic );
          show_help( MENU_HELP, new_topic );
        }
        cur_topic = new_topic;
      }  
      break;
    case ButtonRelease:
      if ( m_ev.BUT == RIGHT_BUTTON || m_ev.BUT == LEFT_BUTTON ) {
        holding_button = false;
        XUnmapWindow( disp, menu_window );
      }
      break;
    case LeaveNotify:
      if ( cur_topic >= 0 && cur_topic < MENU_TOPICS ) {
        highlight_menu_item( cur_topic );
      }
      cur_topic = NULL_TOPIC;
      break;
    default:
      BEEP;
      break;
    }
  } /* end while holding_button */
  
  /* Execute the topic functions */
  switch( cur_topic ) {
  case SAVE:
    save_with_prompt( xc, yc );
    break;
  case READ:
    load_with_prompt( xc, yc );
    break;
  case REDRAW:
    draw_gr();
    break;
  case INIT:
    init_display_attributes();
    break;
  case CLEAR:
    clear_graph_disp();
    break;
  case RUN:
    signal(SIGINT, sig_handler); /* call handler in case of Ctrl-C */
    animat();
    break;
  case QUIT:
    return(true);
    break;
    
  } /* switch cur_topic */
  show_help( OTHER_HELP, GENERAL );
  
  return(false);
  
} /* p_menu() ends */

/** Erases the current graph */
void clear_graph_disp(void) {
  clear_main_canvas();     /* clear the main window */
  clear_graph(0, number_of_vertices);  /* clear the graph structure */
  number_of_vertices = 0;
} /* clear_graph_disp ends */

void sig_handler(int signal_arg) {
  fprintf(stderr, "Signal %d received.\n", signal_arg);
  suspend_animation();
} /* sig_handler ends */

void suspend_animation(void) {
  Window root;
  int x, y;
  unsigned int w, h, bw, d;
  
  XGetGeometry(disp, main_canvas, &root, &x, &y, &w, &h, &bw, &d);
  XMoveWindow(disp,resume_window,(int)w-RES_LEFT,(int)h-RES_UP);
  XMapRaised(disp,resume_window);
  
  while_not_done();
  XUnmapWindow(disp,resume_window);
} /* suspend_animation ends */

/**
   Saves the graph data in a file, prompts for another name if 
   file name is not unique.
*/
void save_with_prompt(int x, int y) {
  FILE * fd;
  char file_name[LINE_LENGTH];  /* when query expects a file name */
  char yes_no[LINE_LENGTH];     /* when query expects a yes or no */
  char msg_buf[MESSAGE_LENGTH]; /* for error messages */
  
  /* Note: query returns false if user hits Ctrl-C */
  if ( ! query( x, y, "Save as (file_name [return])", file_name ) )
    return;
  /* attempt to open file read-only (to see if it already exists) */
  if ( (fd = fopen( file_name, "r" )) != NULL ) { /* File Exists ! */
    fclose( fd );
    BEEP;
    if ( ! query( x, y, "File Exists! Overwrite (y/n[return])?", yes_no ) )
      return;
    if ( strcmp( yes_no, "y" ) )
      return;                   /* quit if user does not want to overwrite
                                   file */
  }
  if( (fd = fopen( file_name, "w" )) == NULL ) {
    sprintf( msg_buf, "Unable to create file %s\n", file_name );
    popup_message( x, y, msg_buf );
    return;
  }
  print_graph_data( fd );
} /* save_with_prompt ends */

void load_with_prompt( int x, int y ) {
  char file_name[LINE_LENGTH];
  FILE * fd;
  int query_return;             /* return value from query (true unless
                                   Ctrl-C has been hit) */
  int error_flag;               /* error flag returned by read_in_gr */
  char msg_buf[MESSAGE_LENGTH];
  query_return = query( x, y, "Read file (name [return])", file_name );
  if ( ! query_return )
    return;
  if ( (fd = fopen( file_name, "r" )) == NULL ) {
    sprintf( msg_buf, "Unable to open file %s\n", file_name );
    popup_message( x, y, msg_buf );
    return;
  } 
  error_flag = read_in_gr( fd );
  if ( ! error_flag ) {
    /* notify the file name in the window title bar and draw the graph */
    char title[LINE_LENGTH];
    strcpy( title, icone );
    strcat( title, " (" );
    strcat( title, file_name );
    strcat( title, ")" );
    XStoreName( disp, main_window, title );
    draw_gr();
  }
  else clear_graph( 0, number_of_vertices );
}

/* menu_init -- initializes the menu */
void menu_init(void) {
  menu_window
    = XCreateSimpleWindow(disp, main_canvas, 0, 0, MW,
                          MENU_ITEM_TOP(MENU_TOPICS + 1, CH_H, VPAD) + 2 * MB,
                          MB, black, white);
  XSelectInput(disp, menu_window,
               OwnerGrabButtonMask | ButtonReleaseMask |
               ButtonMotionMask | LeaveWindowMask);
  XDefineCursor(disp,menu_window,pane_cur);
} /* menu_init() ends */

/** end of Main.c **/

/*  [Last modified: 2007 06 28 at 19:24:25 GMT] */
